import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;

public class LoadBalancer 
{
	private ArrayList<VirtualMachine> vmTypeList;

	public LoadBalancer( ArrayList<VirtualMachine> vmTypeList) {
		
		this.vmTypeList = vmTypeList;

	}
	
	public ArrayList<Integer []> vmPlacement(int [] vmRequest,  ArrayList<Server> serverList,int id0) {
		
		
		ArrayList<Integer []>  serverAllocation= firstFitVmPlacement(vmRequest, serverList,id0);
			
		return serverAllocation;
		
	}
	
	public ArrayList<Integer []> jobPlacement(Job jobRequest,  ArrayList<Server> serverList,int id0) {
		
		
		ArrayList<Integer []>  serverAllocation= firstFitJobPlacement(jobRequest, serverList,id0);
			
		return serverAllocation;
		
	}

	
	public ArrayList<Integer []>  firstFitVmPlacement(int [] vmRequest, ArrayList<Server> serverList, int id0) {
		
		boolean success;
		ArrayList<Integer []> serverAllocation =new ArrayList<Integer []>();
		int vmid=id0;
		for (int i=0; i<vmTypeList.size(); i++) {
			for (int j=0; j<vmRequest[i]; j++) {
				success=false;
				int k=0;
				while(!success & k<serverList.size()) {
					if(serverList.get(k).launchVM(i, vmid)==vmid) {
						success=true;
						Integer [] a={k, i, vmid};
						serverAllocation.add(a);
						vmid++;
					} else
						k++;
				}
			}
		}
		return serverAllocation;
	}
	
	// Loadbalance by assigning VM's to Servers
	// For list of N vm's and N vm id's launches the VM's on servers
	// Balancing done using only CPU resource since this is the most contentious and valuable
	// Objective is to optimally load servers up to 90% capacity
	public ArrayList<Integer []> bestVmPlacement_1(int[] vmRequest, ArrayList<Server> serverList, int[] vmIds) {
		ArrayList<Integer []> serverAllocation = new ArrayList<Integer []>();
		boolean success;
		int temp;
		
		Integer[] cumVmList = new Integer[5];
		ArrayList<Integer>[] cumVmIds = new ArrayList[5];
		ArrayList<Integer> sortVmList = null, sortIdList = null;
		
		//System.out.println(Arrays.toString(vmRequest));
		//System.out.println(Arrays.toString(vmIds));
		
		// Convert explicit vm type and id input into cumulative (helps later with sorting)
		for (int i=0; i<vmRequest.length; i++) {
			cumVmList[vmRequest[i]-1]++;
			cumVmIds[vmRequest[i]-1].add(vmIds[i]);
		}
		
		// Unroll cumulative list based on number of cpu's
		for (int i=vmTypeList.size(); i>=0; i--) {
			for (Integer j=0; j<cumVmList[i]; j++) {
				sortVmList.add(j);
				sortIdList.add(cumVmIds[i].get(j));
			}
		}
		
//		// Debugging
//		for (int i=0; i<sortVmList.size(); i++) {
//			System.out.println(sortVmList.get(i) + " , " + sortIdList.get(i));
//		}
		
		// Begin VM allocation process, keep server CPU's below 90% utilization
		for (int s=0; s<serverList.size(); s++) {
			int v = 0;
			while(v < sortVmList.size()) {
				success=false;
				temp = (int) (serverList.get(s).getvcpu() - Math.ceil(serverList.get(s).getInitCpu() * 0.1)); 
				if (temp >= vmTypeList.get(sortVmList.get(v)).getvcpu()) {
					if(serverList.get(s).launchVM(sortVmList.get(v), sortIdList.get(v))==sortIdList.get(v)) {
						success=true;
						Integer[] a={s, sortVmList.get(v), sortIdList.get(v)};
						serverAllocation.add(a);
						sortVmList.remove(v);
						sortIdList.remove(v);
					}
				}
				if (!success) {
					v++;
				}
			}
		}
		
		// If all vm's assigned then terminate otherwise share the bad news
		if (!sortVmList.isEmpty()) {
			System.out.println("Allocation of VM's failed due to inadequate resources.");
		}

		return serverAllocation;
	}
	
	public ArrayList<Integer []>  firstFitJobPlacement(Job jobRequest, ArrayList<Server> serverList, int id0) {

		boolean success;
		ArrayList<Integer []> serverAllocation =new ArrayList<Integer []>();
		int vmid=id0;
		
		for (int j=0; j<jobRequest.tasks.size(); j++) {
			int i=findvm(jobRequest.tasks.get(j));
			if(i>=0) {
				success=false;
				int k=0;
				
				while(!success & k<serverList.size()) {
					if(serverList.get(k).launchVM(i,vmid)==vmid) {
						success=true;
						Integer [] a={k, i, vmid};
						serverAllocation.add(a);
						jobRequest.tasks.get(j).setVmId(vmid);
						jobRequest.tasks.get(j).setVmType(i);
						vmid++;
					} else
						k++;	
				}
			}				
		}
		return serverAllocation;

	}
	//map  a vm to the task
	public  int findvm(TaskObj t)  {
		
		for (int i=0; i<vmTypeList.size(); i++) 
			if(vmTypeList.get(i).getmemory()>=t.getTaskMemSize() & vmTypeList.get(i).getdisk()>=t.getTaskFileSize() & vmTypeList.get(i).getlan()>=t.getLan())
				return i;
			
		return -1;
		
	}
}